
package com.jf.cloud.flow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.feign.SearchSpuFeignClient;
import com.jf.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.jf.cloud.api.multishop.vo.ShopDetailVO;
import com.jf.cloud.api.order.feign.OrderFeignClient;
import com.jf.cloud.api.order.feign.OrderRefundFeignClient;
import com.jf.cloud.api.order.vo.OrderProdEffectRespVO;
import com.jf.cloud.api.order.vo.OrderRefundProdEffectRespVO;
import com.jf.cloud.api.product.dto.ProdEffectDTO;
import com.jf.cloud.api.product.feign.ProductFeignClient;
import com.jf.cloud.api.product.vo.ProdEffectRespVO;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.util.PageUtil;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.product.vo.SpuVO;
import com.jf.cloud.common.product.vo.search.SpuSearchVO;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.util.Arith;
import com.jf.cloud.common.util.DateUtils;
import com.jf.cloud.common.util.PriceUtil;
import com.jf.cloud.flow.constant.FlowVisitEnum;
import com.jf.cloud.flow.dto.FlowLogDTO;
import com.jf.cloud.flow.mapper.ProductAnalyseMapper;
import com.jf.cloud.flow.mapper.ProductAnalyseUserMapper;
import com.jf.cloud.flow.model.ProductAnalyse;
import com.jf.cloud.flow.model.ProductAnalyseUser;
import com.jf.cloud.flow.service.ProductAnalyseService;
import com.jf.cloud.flow.service.UserAnalysisService;
import com.jf.cloud.flow.vo.FlowProdEffectDataExcelVO;
import com.jf.cloud.flow.vo.FlowProdEffectRespVO;
import com.jf.cloud.flow.vo.ProductFlowInfoVO;
import com.jf.cloud.flow.vo.ShopFlowInfoVO;
import ma.glasnost.orika.MapperFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


/**
 * 流量分析—商品分析
 *
 * @author zz
 * @date 2021-05-21 15:25:19
 */
@Service
public class ProductAnalyseServiceImpl implements ProductAnalyseService {
    private static final Logger log = LoggerFactory.getLogger(ProductAnalyseServiceImpl.class);

    @Autowired
    private ProductAnalyseMapper productAnalyseMapper;
    @Autowired
    private ProductAnalyseUserMapper productAnalyseUserMapper;

    @Autowired
    private UserAnalysisService userAnalysisService;
    @Autowired
    private SearchSpuFeignClient searchSpuFeignClient;
    @Autowired
    private OrderFeignClient orderFeignClient;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private OrderRefundFeignClient orderRefundFeignClient;
    @Autowired
    private ShopDetailFeignClient shopDetailFeignClient;
    @Autowired
    private MapperFacade mapperFacade;
    private final Long VISIT_NUM = 1L;

    @Override
    public PageVO<ProductAnalyse> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> productAnalyseMapper.list());
    }


    @Override
    public ProductAnalyse getByProductAnalyseId(Long productAnalyseId) {
        return productAnalyseMapper.getByProductAnalyseId(productAnalyseId);
    }

    @Override
    public void save(ProductAnalyse productAnalyse) {
        productAnalyseMapper.save(productAnalyse);
    }

    @Override
    public void update(ProductAnalyse productAnalyse) {
        productAnalyseMapper.update(productAnalyse);
    }

    @Override
    public void deleteById(Long productAnalyseId) {
        productAnalyseMapper.deleteById(productAnalyseId);
    }

    @Override
    public void statisticalProduct(List<FlowLogDTO> flowLogList) {
        // 根据系统类型统计各系统之间的数据
        Map<Date, List<FlowLogDTO>> dateMap = flowLogList.stream().collect(Collectors.groupingBy(FlowLogDTO::getCreateTime));
        for (Date date : dateMap.keySet()) {
            statisticalSpu(date, dateMap.get(date));
        }
    }

    private void statisticalSpu(Date date, List<FlowLogDTO> flowLogDTOList) {
        Map<Integer, List<FlowLogDTO>> sysTypeMap = flowLogDTOList.stream().collect(Collectors.groupingBy(FlowLogDTO::getSystemType));
        Set<Long> spuIdSet = flowLogDTOList.stream().map(flowLogDTO -> Long.valueOf(flowLogDTO.getBizData())).collect(Collectors.toSet());
        List<ProductAnalyse> productAnalyses = productAnalyseMapper.listByDate(date, spuIdSet);
        Map<Integer, List<ProductAnalyse>> sysTypeDbMap = productAnalyses.stream().collect(Collectors.groupingBy(ProductAnalyse::getSystemType));
        // 获取还未保存的商品信息（未存到数据库）
        Map<Long, SpuSearchVO> spuSearchMap = getSpuMap(spuIdSet);
        // 根据系统统计对应的商品数据
        for (Integer sysType : sysTypeMap.keySet()) {
            // 数据库中已有的商品信息
            Map<Long, ProductAnalyse> spuDbMap;
            if (sysTypeDbMap.containsKey(sysType)) {
                spuDbMap = sysTypeDbMap.get(sysType).stream().collect(Collectors.toMap(ProductAnalyse::getSpuId, p -> p));
            } else {
                spuDbMap = new HashMap<>(0);
            }
            // 获取当前系统的用户操作数据
            List<FlowLogDTO> productList = sysTypeMap.get(sysType).stream().collect(Collectors.toList());
            // 根据商品id进行分组
            Map<String, List<FlowLogDTO>> spuMap = productList.stream().collect(Collectors.groupingBy(FlowLogDTO::getBizData));
            // 根据商品统计各个商品的信息
            for (String key : spuMap.keySet()) {
                Long spuId = Long.valueOf(key);
                // 获取该商品存储在数据库中的统计数据
                ProductAnalyse productAnalyse = spuDbMap.get(spuId);
                // 若数据库还没该商品的统计数据，则初始化一个
                if (Objects.isNull(productAnalyse)) {
                    // 若没有该商品信息，则不进行统计
                    if (Objects.isNull(spuSearchMap.get(spuId))) {
                        continue;
                    }
                    productAnalyse = productAnalyseInit(date, spuSearchMap.get(spuId));
                    productAnalyse.setSystemType(sysType);
                    spuDbMap.put(spuId, productAnalyse);
                    productAnalyses.add(productAnalyse);
                }
                // 根据用户id进行分组
                Map<String, ProductAnalyseUser> userMap = productAnalyse.getProductAnalyseUsers().stream().collect(Collectors.toMap(ProductAnalyseUser::getUserId, p -> p));
                // 获取用户在该商品中的所有操作信息
                List<FlowLogDTO> spuFlowLogs = spuMap.get(key);
                // 处理用户在商品中操作的信息
                for (FlowLogDTO flowLogDTO : spuFlowLogs) {
                    handleUserOperation(productAnalyse, flowLogDTO, userMap);
                }
            }
        }
        // 保存、更新统计的信息
        saveOrUpdate(productAnalyses);
    }

    /**
     * 保存、更新统计的信息
     *
     * @param productAnalyses
     */
    private void saveOrUpdate(List<ProductAnalyse> productAnalyses) {
        List<ProductAnalyse> productSaveList = new ArrayList<>();
        List<ProductAnalyse> productUpdateList = new ArrayList<>();
        // 商品数据处理
        for (ProductAnalyse productAnalysis : productAnalyses) {
            if (Objects.isNull(productAnalysis.getProductAnalyseId())) {
                productSaveList.add(productAnalysis);
                continue;
            }
            productUpdateList.add(productAnalysis);
        }
        List<ProductAnalyseUser> userSaveList = new ArrayList<>();
        List<ProductAnalyseUser> userUpdateList = new ArrayList<>();
        if (CollUtil.isNotEmpty(productSaveList)) {
            productAnalyseMapper.saveBatch(productSaveList);
        }
        if (CollUtil.isNotEmpty(productUpdateList)) {
            productAnalyseMapper.updateBatch(productUpdateList);
        }

        // 用户的商品操作数据处理
        for (ProductAnalyse productAnalyse : productAnalyses) {
            for (ProductAnalyseUser productAnalyseUser : productAnalyse.getProductAnalyseUsers()) {
                if (Objects.isNull(productAnalyseUser.getProductAnalyseUserId())) {
                    productAnalyseUser.setCreateDate(productAnalyse.getCreateDate());
                    productAnalyseUser.setProductAnalyseId(productAnalyse.getProductAnalyseId());
                    userSaveList.add(productAnalyseUser);
                    continue;
                }
                userUpdateList.add(productAnalyseUser);
            }
        }
        if (CollUtil.isNotEmpty(userSaveList)) {
            productAnalyseUserMapper.saveBatch(userSaveList);
        }
        if (CollUtil.isNotEmpty(userUpdateList)) {
            productAnalyseUserMapper.updateBatch(userUpdateList);
        }
    }

    /**
     * 处理用户操作信息
     *
     * @param productAnalyse
     * @param flowLogDTO
     * @param userMap
     */
    private void handleUserOperation(ProductAnalyse productAnalyse, FlowLogDTO flowLogDTO, Map<String, ProductAnalyseUser> userMap) {
        ProductAnalyseUser productAnalyseUser;
        if (userMap.containsKey(flowLogDTO.getUuid())) {
            productAnalyseUser = userMap.get(flowLogDTO.getUuid());
            // 用户有userId(登陆了)
            if (StrUtil.isNotBlank(flowLogDTO.getUserId())) {
                userMap.remove(flowLogDTO.getUuid());
                mergerUserOperation(flowLogDTO.getUserId(), userMap, productAnalyseUser);
            }
        } else if (userMap.containsKey(flowLogDTO.getUserId())) {
            productAnalyseUser = userMap.get(flowLogDTO.getUserId());
        } else {
            productAnalyseUser = productAnalyseUserInit(flowLogDTO);
            productAnalyseUser.setSpuId(productAnalyse.getSpuId());
            userMap.put(productAnalyseUser.getUserId(), productAnalyseUser);
            productAnalyse.getProductAnalyseUsers().add(productAnalyseUser);
        }

        // 商品操作信息统计
        if (Objects.equals(flowLogDTO.getVisitType(), FlowVisitEnum.VISIT.value()) || Objects.equals(flowLogDTO.getVisitType(), FlowVisitEnum.SHARE.value())) {
            productAnalyse.setVisis(productAnalyse.getVisis() + VISIT_NUM);
            if (!Objects.equals(productAnalyseUser.getIsVisit(), 1)) {
                productAnalyseUser.setIsVisit(1);
            }
        } else if (Objects.equals(flowLogDTO.getVisitType(), FlowVisitEnum.CLICK.value())) {
            productAnalyse.setClick(productAnalyse.getClick() + flowLogDTO.getNums());
            if (!Objects.equals(productAnalyseUser.getIsClick(), 1)) {
                productAnalyseUser.setIsClick(1);
            }
        } else if (Objects.equals(flowLogDTO.getVisitType(), FlowVisitEnum.SHOP_CAT.value())) {
            log.info("你为啥报错了："+flowLogDTO.toString());
            productAnalyse.setPlusShopCart(productAnalyse.getPlusShopCart() + flowLogDTO.getNums());
            if (!Objects.equals(productAnalyseUser.getIsPlusShopCart(), 1)) {
                productAnalyseUser.setIsPlusShopCart(1);
            }
        }
    }

    /**
     * 合并用户操作数据
     *
     * @param userId
     * @param userMap
     * @param productAnalyseUser
     */
    private void mergerUserOperation(String userId, Map<String, ProductAnalyseUser> userMap, ProductAnalyseUser productAnalyseUser) {
        // 若未有该用户信息，则直接插入
        if (!userMap.containsKey(userId)) {
            productAnalyseUser.setUserId(userId);
            userMap.put(userId, productAnalyseUser);
            return;
        }
        // 若已有该用户信息，则合并信息
        ProductAnalyseUser productAnalyseUserNew = userMap.get(userId);
        if (Objects.equals(productAnalyseUser.getIsClick(), 1)) {
            productAnalyseUserNew.setIsClick(1);
        }
        if (Objects.equals(productAnalyseUser.getIsVisit(), 1)) {
            productAnalyseUserNew.setIsVisit(1);
        }
        if (Objects.equals(productAnalyseUser.getIsPlusShopCart(), 1)) {
            productAnalyseUserNew.setIsPlusShopCart(1);
        }
    }


    /**
     * 初始化商品用户操作类
     *
     * @param flowLogDTO
     * @return
     */
    private ProductAnalyseUser productAnalyseUserInit(FlowLogDTO flowLogDTO) {
        ProductAnalyseUser productAnalyseUser = new ProductAnalyseUser();
        if (StrUtil.isNotBlank(flowLogDTO.getUserId())) {
            productAnalyseUser.setUserId(flowLogDTO.getUserId());
        } else {
            productAnalyseUser.setUserId(flowLogDTO.getUuid());
        }
        productAnalyseUser.setIsVisit(0);
        productAnalyseUser.setIsClick(0);
        productAnalyseUser.setIsPlusShopCart(0);
        return productAnalyseUser;
    }

    /**
     * 获取还未保存的商品信息（未存到数据库）
     *
     * @param spuIds 所有商品的id
     * @return
     */
    private Map<Long, SpuSearchVO> getSpuMap(Set<Long> spuIds) {
        ServerResponseEntity<List<SpuSearchVO>> spuResponse = searchSpuFeignClient.listSpuBySpuIds(new ArrayList<>(spuIds));
        Map<Long, SpuSearchVO> spuMap = spuResponse.getData().stream().collect(Collectors.toMap(SpuSearchVO::getSpuId, s -> s));
        return spuMap;
    }

    private ProductAnalyse productAnalyseInit(Date date, SpuSearchVO spuVO) {
        Long defaultValue = 0L;
        ProductAnalyse productAnalyse = new ProductAnalyse();
        productAnalyse.setSpuId(spuVO.getSpuId());
        productAnalyse.setShopId(spuVO.getShopId());
        productAnalyse.setCreateDate(date);
        productAnalyse.setVisis(defaultValue);
        productAnalyse.setClick(defaultValue);
        productAnalyse.setShareVisit(defaultValue);
        productAnalyse.setPlusShopCart(0);
        productAnalyse.setProductAnalyseUsers(new ArrayList<>());
        return productAnalyse;
    }

    @Override
    public PageVO<ProdEffectRespVO> getProductEffect(PageDTO pageDTO, ProdEffectDTO prodEffectDTO) {
        Long startTime = prodEffectDTO.getStartTime().getTime();
        Long endTime = prodEffectDTO.getEndTime().getTime();
        // 是否按指定字段排序，是就不先进行分页（后续按字段排序再进行分页），不是就直接分页
        PageVO<ProdEffectRespVO> flowSpuPage = null;
        List<ProdEffectRespVO> prodEffects;
        if (prodEffectDTO.getSortParam() != null && prodEffectDTO.getSortType() != null) {
            prodEffects = productAnalyseMapper.getProdEffectRespByTime(new Date(startTime), new Date(endTime), prodEffectDTO.getShopId());
        } else {
            flowSpuPage = getFlowSpuId(pageDTO, startTime, endTime, prodEffectDTO.getShopId());
            prodEffects = flowSpuPage.getList();
        }
        Map<Long, ProdEffectRespVO> prodEffectMap = prodEffects.stream().collect(Collectors.toMap(ProdEffectRespVO::getSpuId, x -> x));;
        List<Long> spuIds = new ArrayList<>(prodEffectMap.keySet());;
        // 空数据直接返回
        if (CollUtil.isEmpty(spuIds)) {
            flowSpuPage = new PageVO<>();
            flowSpuPage.setPages(0);
            flowSpuPage.setTotal(0L);
            flowSpuPage.setList(new ArrayList<>());
            return flowSpuPage;
        }
        // 若是按指定字段排序(flowSpuPage为空)，则手动构建分页参数
        List<FlowProdEffectRespVO> flowProdList = null;
        List<OrderProdEffectRespVO> prodOrderList = null;
        List<OrderRefundProdEffectRespVO> prodOrderRefundList = null;
        if (flowSpuPage == null) {
            flowSpuPage = new PageVO<>();
            int pages = prodEffects.size() / pageDTO.getPageSize();
            flowSpuPage.setPages(prodEffects.size() % pageDTO.getPageSize() == 0 ? pages : pages + 1);
            flowSpuPage.setTotal(Long.valueOf(String.valueOf(prodEffects.size())));
            // 按照指定的排序字段进行分页
            List<Long> finalSpuIds = spuIds;
            switch (prodEffectDTO.getSortParam()) {
                case 0:
                case 1:
                case 2:
                case 3:
                    // 直接sql进行排序分页
                    PageVO<FlowProdEffectRespVO> page = PageUtil.doPage(pageDTO, () -> productAnalyseMapper.getProdEffectByParam(finalSpuIds, new Date(startTime), new Date(endTime), prodEffectDTO));
                    flowProdList = page.getList();
                    spuIds = flowProdList.stream().map(FlowProdEffectRespVO::getSpuId).collect(Collectors.toList());
                    break;
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                    // 全部数据加载，stream流进行排序分页
                    prodOrderList = this.sortAndPageProdOrder(startTime, endTime, spuIds, pageDTO, prodEffectDTO);
                    spuIds = prodOrderList.stream().map(OrderProdEffectRespVO::getSpuId).collect(Collectors.toList());
                    break;
                case 10:
                case 11:
                case 12:
                case 13:
                case 14:
                    // 全部数据加载，stream流进行排序分页
                    prodOrderRefundList = this.sortAndPageOrderRefund(startTime, endTime, spuIds, pageDTO, prodEffectDTO);
                    spuIds = prodOrderRefundList.stream().map(OrderRefundProdEffectRespVO::getSpuId).collect(Collectors.toList());
                    break;
                case 15:
                    // 退款率单独拿出进行排序
                    prodOrderRefundList = this.sortAndPageRefundSuccessRate(prodOrderList, prodOrderRefundList, startTime, endTime, spuIds, pageDTO, prodEffectDTO);
                    spuIds = prodOrderRefundList.stream().map(OrderRefundProdEffectRespVO::getSpuId).collect(Collectors.toList());
                    break;
                default:
                    throw new LuckException("sortParam错误");
            }
        }
        //处理商品数据
        handleProdData(startTime, endTime, flowSpuPage, prodEffectMap, spuIds, flowProdList, prodOrderList, prodOrderRefundList);
        return flowSpuPage;
    }

    private void handleProdData(Long startTime, Long endTime, PageVO<ProdEffectRespVO> flowSpuPage, Map<Long, ProdEffectRespVO> prodEffectMap, List<Long> spuIds, List<FlowProdEffectRespVO> flowProdList, List<OrderProdEffectRespVO> prodOrderList, List<OrderRefundProdEffectRespVO> prodOrderRefundList) {
        //根据参数获取商品信息
        List<SpuSearchVO> spuSearchVOs = searchSpuFeignClient.listNotDeleteSpu(spuIds).getData();
        Map<Long, SpuSearchVO> searchVOMap = spuSearchVOs.stream().collect(Collectors.toMap(SpuSearchVO::getSpuId, x -> x));
        //根据参数获取商品相关数据
        if (flowProdList == null) {
            flowProdList = productAnalyseMapper .getProdEffectByParam(spuIds, new Date(startTime), new Date(endTime), null);
        }
        Map<Long, FlowProdEffectRespVO> flowProdMap = flowProdList.stream().collect(Collectors.toMap(FlowProdEffectRespVO::getSpuId, p -> p));
        //根据参数获取商品订单数据分析
        if (prodOrderList == null) {
            prodOrderList = orderFeignClient.getProdEffectByDateAndProdIds(spuIds, startTime, endTime).getData();
        }
        Map<Long, OrderProdEffectRespVO> prodOrderMap = prodOrderList.stream().collect(Collectors.toMap(OrderProdEffectRespVO::getSpuId, p -> p));
        //根据参数获取商品退款订单数据分析
        if (prodOrderRefundList == null) {
            prodOrderRefundList = orderRefundFeignClient.getProdRefundEffectByDateAndProdIds(spuIds, startTime, endTime).getData();
        }
        Map<Long, OrderRefundProdEffectRespVO> prodOrderRefundMap = prodOrderRefundList.stream().collect(Collectors.toMap(OrderRefundProdEffectRespVO::getSpuId, p -> p));
        // 整合并构建数据
        List<ProdEffectRespVO> prodEffectRespVOS = new ArrayList<>();
        for (Long spuId : spuIds) {
            ProdEffectRespVO prodEffectRespVO = prodEffectMap.get(spuId);
            if (Objects.isNull(prodEffectRespVO)) {
                prodEffectRespVO = new ProdEffectRespVO();
                prodEffectRespVO.setSpuId(spuId);
            }
            SpuSearchVO spuSearchVO = searchVOMap.get(spuId);
            loadProdEffectRespData(spuSearchVO, flowProdMap, prodOrderMap, prodOrderRefundMap, prodEffectRespVO);
            prodEffectRespVOS.add(prodEffectRespVO);
        }
        flowSpuPage.setList(prodEffectRespVOS);
    }

    /**
     * 按指定字段对订单数据进行排序分页
     */
    private List<OrderProdEffectRespVO> sortAndPageProdOrder(Long startTime,
                                                             Long endTime,
                                                             List<Long> spuIds,
                                                             PageDTO pageDTO,
                                                             ProdEffectDTO prodEffectDTO) {
        List<OrderProdEffectRespVO> prodOrderList = orderFeignClient.getProdEffectByDateAndProdIds(spuIds, startTime, endTime).getData();
        // 填补空数据
        if (prodOrderList.size() < spuIds.size()) {
            Set<Long> existSpuIds = prodOrderList.stream().map(OrderProdEffectRespVO::getSpuId).collect(Collectors.toSet());
            for (Long spuId : spuIds) {
                if (existSpuIds.add(spuId)) {
                    prodOrderList.add(new OrderProdEffectRespVO(spuId, 0, 0,  0, 0, 0.0, 0.0, 0));
                }
            }
        }
        // stream流分页排序
        prodOrderList = prodOrderList.stream().sorted((o1, o2) -> {
            int compare;
            switch (prodEffectDTO.getSortParam()) {
                case 4:
                    compare = o1.getPlaceOrderPerson().compareTo(o2.getPlaceOrderPerson());
                    break;
                case 5:
                    compare = o1.getPayPerson().compareTo(o2.getPayPerson());
                    break;
                case 6:
                    compare = o1.getPayOrderNum().compareTo(o2.getPlaceOrderNum());
                    break;
                case 7:
                    compare = o1.getPayNum().compareTo(o2.getPayNum());
                    break;
                case 8:
                    compare = o1.getPlaceOrderAmount().compareTo(o2.getPlaceOrderAmount());
                    break;
                default:
                    compare = o1.getPayAmount().compareTo(o2.getPayAmount());
            }
            // 数值相等则比较id大小
            if (compare == 0) {
                compare = o1.getSpuId().compareTo(o2.getSpuId());
            }
            return prodEffectDTO.getSortType() == 0 ? -1 * compare : compare;
        }).skip((long) (pageDTO.getPageNum() - 1) * pageDTO.getPageSize()).limit(pageDTO.getPageSize()).collect(Collectors.toList());
        return prodOrderList;
    }

    /**
     * 按指定字段对退款数据进行排序分页
     */
    private List<OrderRefundProdEffectRespVO> sortAndPageOrderRefund(Long startTime,
                                                                     Long endTime,
                                                                     List<Long> spuIds,
                                                                     PageDTO pageDTO,
                                                                     ProdEffectDTO prodEffectDTO) {
        List<OrderRefundProdEffectRespVO> prodOrderRefundList = orderRefundFeignClient.getProdRefundEffectByDateAndProdIds(spuIds, startTime, endTime).getData();
        // 填补空数据
        if (prodOrderRefundList.size() < spuIds.size()) {
            Set<Long> existSpuIds = prodOrderRefundList.stream().map(OrderRefundProdEffectRespVO::getSpuId).collect(Collectors.toSet());
            for (Long spuId : spuIds) {
                if (existSpuIds.add(spuId)) {
                    prodOrderRefundList.add(new OrderRefundProdEffectRespVO(spuId, 0, 0, 0, 0, 0.0, 0.0));
                }
            }
        }
        prodOrderRefundList = prodOrderRefundList.stream().sorted((o1, o2) -> {
            int compare;
            switch (prodEffectDTO.getSortParam()) {
                case 10:
                    compare = o1.getRefundNum().compareTo(o2.getRefundNum());
                    break;
                case 11:
                    compare = o1.getRefundPerson().compareTo(o2.getRefundPerson());
                    break;
                case 12:
                    compare = o1.getRefundSuccessNum().compareTo(o2.getRefundSuccessNum());
                    break;
                case 13:
                    compare = o1.getRefundSuccessPerson().compareTo(o2.getRefundSuccessPerson());
                    break;
                case 14:
                    compare = o1.getRefundSuccessAmount().compareTo(o2.getRefundSuccessAmount());
                    break;
                default:
                    // TODO:退款率暂不处理（不太好处理，需要用到下单数据进行计算）
                    compare = o1.getRefundSuccessRate().compareTo(o2.getRefundSuccessRate());
            }
            // 数值相等则比较id大小
            if (compare == 0) {
                compare = o1.getSpuId().compareTo(o2.getSpuId());
            }
            return prodEffectDTO.getSortType() == 0 ? -1 * compare : compare;
        }).skip((long) (pageDTO.getPageNum() - 1) * pageDTO.getPageSize()).limit(pageDTO.getPageSize()).collect(Collectors.toList());
        return prodOrderRefundList;
    }

    /**
     * 对退款率进行排序分页
     * @param prodOrderList
     * @param prodOrderRefundList
     */
    private List<OrderRefundProdEffectRespVO> sortAndPageRefundSuccessRate(List<OrderProdEffectRespVO> prodOrderList,
                                                                           List<OrderRefundProdEffectRespVO> prodOrderRefundList,
                                                                           Long startTime,
                                                                           Long endTime,
                                                                           List<Long> spuIds,
                                                                           PageDTO pageDTO,
                                                                           ProdEffectDTO prodEffectDTO) {
        // 查退款数据
        prodOrderRefundList = orderRefundFeignClient.getProdRefundEffectByDateAndProdIds(spuIds, startTime, endTime).getData();
        if (prodOrderRefundList.size() < spuIds.size()) {
            Set<Long> existSpuIds = prodOrderRefundList.stream().map(OrderRefundProdEffectRespVO::getSpuId).collect(Collectors.toSet());
            for (Long spuId : spuIds) {
                if (existSpuIds.add(spuId)) {
                    prodOrderRefundList.add(new OrderRefundProdEffectRespVO(spuId, 0, 0, 0, 0, 0.0, 0.0));
                }
            }
        }
        // 查下单数据
        prodOrderList = orderFeignClient.getProdEffectByDateAndProdIds(spuIds, startTime, endTime).getData();
        Map<Long, OrderProdEffectRespVO> prodOrderMap = prodOrderList.stream().collect(Collectors.toMap(OrderProdEffectRespVO::getSpuId, x -> x));
        // 退款数据补充退款率
        OrderProdEffectRespVO prodOrder;
        for (OrderRefundProdEffectRespVO prodOrderRefund : prodOrderRefundList) {
            prodOrder = prodOrderMap.get(prodOrderRefund.getSpuId());
            if (prodOrderRefund.getRefundSuccessNum() == 0
                    || prodOrder == null) {
                prodOrderRefund.setRefundSuccessRate(0.0);
            } else {
                prodOrderRefund.setRefundSuccessRate(divAverage(prodOrderRefund.getRefundSuccessNum(), prodOrder.getPayOrderNum(), 4));
            }
        }
        // stream流分页排序
        prodOrderRefundList = prodOrderRefundList.stream().sorted((o1, o2) -> {
            int compare = o1.getRefundSuccessRate().compareTo(o2.getRefundSuccessRate());
            if (compare == 0) {
                compare = o1.getSpuId().compareTo(o2.getSpuId());
            }
            return prodEffectDTO.getSortType() == 0 ? -1 * compare : compare;
        }).skip((long) (pageDTO.getPageNum() - 1) * pageDTO.getPageSize()).limit(pageDTO.getPageSize()).collect(Collectors.toList());
        return prodOrderRefundList;
    }

    @Override
    public List<FlowProdEffectDataExcelVO> listProdEffectExcel(PageDTO pageDTO, ProdEffectDTO prodEffectDTO) {
        PageVO<ProdEffectRespVO> productEffect = getProductEffect(pageDTO, prodEffectDTO);
        List<FlowProdEffectDataExcelVO> excelList = mapperFacade.mapAsList(productEffect.getList(), FlowProdEffectDataExcelVO.class);
        for (FlowProdEffectDataExcelVO prodEffectData : excelList) {
            prodEffectData.setPrice(PriceUtil.toDecimalPrice(prodEffectData.getPrice()).longValue());
            prodEffectData.setPlaceOrderAmount(PriceUtil.toDecimalPrice(prodEffectData.getPlaceOrderAmount().longValue()).doubleValue());
            prodEffectData.setPayAmount(PriceUtil.toDecimalPrice(prodEffectData.getPayAmount().longValue()).doubleValue());
            prodEffectData.setRefundSuccessAmount(PriceUtil.toDecimalPrice(prodEffectData.getRefundSuccessAmount().longValue()).doubleValue());
        }
        return excelList;
    }

    private void loadProdEffectRespData(SpuSearchVO spuSearchVO, Map<Long, FlowProdEffectRespVO> flowProdMap, Map<Long, OrderProdEffectRespVO> prodOrderMap, Map<Long, OrderRefundProdEffectRespVO> prodOrderRefundMap, ProdEffectRespVO param) {
        if (Objects.nonNull(spuSearchVO)) {
            //商品名称
            param.setSpuName(spuSearchVO.getSpuName());
            //商品图片
            param.setSpuUrl(spuSearchVO.getMainImgUrl());
            //商品价格
            param.setPrice(spuSearchVO.getPriceFee());
            //店铺名称
            param.setShopName(spuSearchVO.getShopName());
        }
        //商品相关数据
        FlowProdEffectRespVO flowProdEffectRespVO = flowProdMap.getOrDefault(param.getSpuId(), new FlowProdEffectRespVO());
        //曝光人数
        param.setExposePersonNum(getDefault(flowProdEffectRespVO.getExposePersonNum()));
        //加购人数
        param.setAddCartPerson(getDefault(flowProdEffectRespVO.getAddCartPerson()));
        //曝光次数
        param.setExpose(getDefault(flowProdEffectRespVO.getExpose()));
        //加购件数
        param.setAddCart(getDefault(flowProdEffectRespVO.getAddCart()));
        //订单相关数据
        OrderProdEffectRespVO orderProdEffectRespVO = prodOrderMap.getOrDefault(param.getSpuId(), new OrderProdEffectRespVO());
        //下单人数
        param.setPlaceOrderPerson(getDefault(orderProdEffectRespVO.getPlaceOrderPerson()));
        //支付人数
        param.setPayPerson(getDefault(orderProdEffectRespVO.getPayPerson()));
        //下单件数
        param.setPlaceOrderNum(getDefault(orderProdEffectRespVO.getPlaceOrderNum()));
        //支付件数
        param.setPayNum(getDefault(orderProdEffectRespVO.getPayNum()));
        //下单金额
        param.setPlaceOrderAmount(getDefault(orderProdEffectRespVO.getPlaceOrderAmount()));
        //支付金额
        param.setPayAmount(getDefault(orderProdEffectRespVO.getPayAmount()));
        //退款订单相关数据
        OrderRefundProdEffectRespVO orderRefundProdEffectRespVO = prodOrderRefundMap.getOrDefault(param.getSpuId(), new OrderRefundProdEffectRespVO());
        //申请退款订单数
        param.setRefundNum(getDefault(orderRefundProdEffectRespVO.getRefundNum()));
        //申请退款人数
        param.setRefundPerson(getDefault(orderRefundProdEffectRespVO.getRefundPerson()));
        //成功退款订单数
        param.setRefundSuccessNum(getDefault(orderRefundProdEffectRespVO.getRefundSuccessNum()));
        //成功退款人数
        param.setRefundSuccessPerson(getDefault(orderRefundProdEffectRespVO.getRefundSuccessPerson()));
        //成功退款金额
        param.setRefundSuccessAmount(getDefault(orderRefundProdEffectRespVO.getRefundSuccessAmount()));
        //退款率 = 成功退款订单数/支付成功数
        param.setRefundSuccessRate(divAverage(getDefault(param.getRefundSuccessNum()), getDefault(orderProdEffectRespVO.getPayOrderNum()), 4));

    }

    @Override
    public List<ShopFlowInfoVO> listShopRankIngByFlow(DateTime endTime, Integer dayCount, Integer limit) {
        Date startTime = DateUtils.getBeforeDay(endTime, -dayCount);
        List<ShopFlowInfoVO> shopFlowInfoVOList = productAnalyseMapper.listShopRankIngByFlow(startTime, endTime, limit);
        if (CollUtil.isEmpty(shopFlowInfoVOList)) {
            return shopFlowInfoVOList;
        }
        List<Long> shopIds = shopFlowInfoVOList.stream().map(ShopFlowInfoVO::getShopId).collect(Collectors.toList());
        ServerResponseEntity<List<ShopDetailVO>> listServerResponseEntity = shopDetailFeignClient.listByShopIds(shopIds);
        if (listServerResponseEntity.isFail()) {
            throw new LuckException(listServerResponseEntity.getMsg());
        }
        if (CollUtil.isEmpty(listServerResponseEntity.getData())) {
            return new ArrayList<>();
        }
        // 赋值店铺名称
        Map<Long, ShopDetailVO> shopDetailMap = listServerResponseEntity.getData().stream().collect(Collectors.toMap(ShopDetailVO::getShopId, p -> p));
        Iterator<ShopFlowInfoVO> iterator = shopFlowInfoVOList.iterator();
        while (iterator.hasNext()) {
            ShopFlowInfoVO shopFlowInfoVO = iterator.next();
            if (Objects.isNull(shopDetailMap.get(shopFlowInfoVO.getShopId()))) {
                iterator.remove();
                continue;
            }
            shopFlowInfoVO.setShopName(shopDetailMap.get(shopFlowInfoVO.getShopId()).getShopName());
        }
        return shopFlowInfoVOList;
    }

    /**
     * 获取供应商商品访问流量排行榜
     *
     * @param spuVOList 代销商品列表
     * @param endTime   结束时间
     * @param dayCount  天数
     * @param limit     条数
     * @return
     */
    public List<ProductFlowInfoVO> listSupplierProdRankIngByFlow(List<SpuVO> spuVOList, DateTime endTime, Integer dayCount, Integer limit) {
        Date startTime = DateUtils.getBeforeDay(endTime, -dayCount);

        // 被代销的商品Map （一个供应商商品Id -->  多个代销商家商品Id）
        Map<Long, Long> consignmentSpuMap = spuVOList.stream().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getSupplierSpuId));
        // 代销商家商品Id的访问和交易数据（有重复spuId的List）
        List<ProductFlowInfoVO> merchantProductFlowInfoVOList = productAnalyseMapper.getVisitAndTransactionDataBySpuIds(startTime, endTime, consignmentSpuMap.keySet());
        // 按spuId分组的数据
        Map<Long, List<ProductFlowInfoVO>> merchantProductFlowInfoMap = merchantProductFlowInfoVOList.stream().collect(Collectors.groupingBy(ProductFlowInfoVO::getSpuId));

        // 记录供应商商品的浏览量Map
        Map<Long, Map<String, Integer>> supplierProdFlowMap = new HashMap<>(consignmentSpuMap.size());

        for (Map.Entry<Long, List<ProductFlowInfoVO>> productFlowInfo : merchantProductFlowInfoMap.entrySet()) {
            ProductFlowInfoVO spuFlowAndPayInfo = new ProductFlowInfoVO();
            Long merchantSpuId = productFlowInfo.getKey();
            spuFlowAndPayInfo.setSpuId(merchantSpuId);
            Long supplierSpuId = consignmentSpuMap.get(merchantSpuId);

            // 用户支付数据Map
            Map<String, Integer> userPayInfoMap = supplierProdFlowMap.get(supplierSpuId);
            if (Objects.isNull(userPayInfoMap)) {
                userPayInfoMap = new HashMap<>();
                supplierProdFlowMap.put(supplierSpuId, userPayInfoMap);
            }

            for (ProductFlowInfoVO productFlowInfoVO : productFlowInfo.getValue()) {
                String userId = productFlowInfoVO.getUserId();
                Long payAmount = productFlowInfoVO.getPayAmount();
                if (!userPayInfoMap.containsKey(userId)) {
                    userPayInfoMap.put(userId, 0);
                }
                if (payAmount > 0L) {
                    userPayInfoMap.put(userId, 1);
                }
            }
        }
        // 供应商商品的访问和交易数据List
        List<ProductFlowInfoVO> supplierProductFlowInfoVOList = new ArrayList<>(supplierProdFlowMap.size());
        for (Map.Entry<Long, Map<String, Integer>> supplierProdFlowEntry : supplierProdFlowMap.entrySet()) {
            ProductFlowInfoVO supplierProductFlow = new ProductFlowInfoVO();

            Long supplierSpuId = supplierProdFlowEntry.getKey();
            supplierProductFlow.setSpuId(supplierSpuId);
            Map<String, Integer> userPayInfoMap = supplierProdFlowEntry.getValue();
            // 访客数量
            supplierProductFlow.setVisitUserCount((long) userPayInfoMap.keySet().size());
            // 供应商商品用户支付数
            long payUserCount = userPayInfoMap.values().parallelStream().filter(payAmount -> payAmount > 0).count();
            supplierProductFlow.setPayUserCount(payUserCount);
            supplierProductFlowInfoVOList.add(supplierProductFlow);
        }

        // 对供应商的商品访客数进行倒序排序
        List<ProductFlowInfoVO> sortedProductFlowInfoVOList = supplierProductFlowInfoVOList.stream().sorted(Comparator.comparing(ProductFlowInfoVO::getVisitUserCount).thenComparing(ProductFlowInfoVO::getPayUserCount).reversed()).limit(limit).collect(Collectors.toList());
        List<Long> spuIdList = sortedProductFlowInfoVOList.stream().map(ProductFlowInfoVO::getSpuId).collect(Collectors.toList());

        // 获取商品的名称和图片信息
        ServerResponseEntity<List<SpuSearchVO>> spuResponseEntity = searchSpuFeignClient.listSpuBySpuIds(spuIdList);
        if (spuResponseEntity.isFail()) {
            throw new LuckException(spuResponseEntity.getMsg());
        }
        List<SpuSearchVO> spuSearchVOList = spuResponseEntity.getData();
        Map<Long, SpuSearchVO> spuSearchVOMap = spuSearchVOList.stream().collect(Collectors.toMap(SpuSearchVO::getSpuId, spuSearchVO -> spuSearchVO));

        List<ProductFlowInfoVO> resultProductFlowList = new ArrayList<>(limit);
        for (ProductFlowInfoVO productFlowInfoVO : sortedProductFlowInfoVOList) {
            if (Objects.nonNull(spuSearchVOMap.get(productFlowInfoVO.getSpuId()))) {
                productFlowInfoVO.setSpuName(spuSearchVOMap.get(productFlowInfoVO.getSpuId()).getSpuName());
                productFlowInfoVO.setSpuImg(spuSearchVOMap.get(productFlowInfoVO.getSpuId()).getMainImgUrl());
                productFlowInfoVO.setPayRate(divAverage(productFlowInfoVO.getPayUserCount().intValue(), productFlowInfoVO.getVisitUserCount().intValue(), 4));
                resultProductFlowList.add(productFlowInfoVO);
            }
        }
        return resultProductFlowList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSpuDataBySpuId(Long spuId) {
        if (Objects.isNull(spuId)) {
            return;
        }
        productAnalyseUserMapper.deleteSpuDataBySpuId(spuId);
        productAnalyseMapper.deleteSpuDataBySpuId(spuId);
    }

    /**
     * 获取供应商店铺今天商品浏览量和新老访客数量，代销商品的总流量等于供应商的流量
     *
     * @param spuIds 代销商品Id
     * @return
     */
    public ShopFlowInfoVO getSupplierFlowInfo(List<Long> spuIds) {
        Long todayProductVisitCount = productAnalyseMapper.getProductVisitCount(DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date()), spuIds);
        Long yesterdayProductVisitCount = productAnalyseMapper.getProductVisitCount(DateUtil.beginOfDay(DateUtil.yesterday()), DateUtil.endOfDay(DateUtil.yesterday()), spuIds);

        ShopFlowInfoVO shopFlowInfoVO = new ShopFlowInfoVO();
        shopFlowInfoVO.setVisitCount(Objects.isNull(todayProductVisitCount) ? 0 : todayProductVisitCount);
        shopFlowInfoVO.setYesterdayVisitCount(Objects.isNull(yesterdayProductVisitCount) ? 0 : yesterdayProductVisitCount);

        List<String> shopUserIds = productAnalyseUserMapper.getVisitUserIdsInPastTenDays(DateUtil.offsetDay(DateUtil.beginOfDay(DateUtil.date()), -9), DateUtil.endOfDay(DateUtil.date()), spuIds);
        List<String> newUserIds = userAnalysisService.getNewVisitUserIdsInPastTenDays(DateUtil.offsetDay(DateUtil.beginOfDay(DateUtil.date()), -9), DateUtil.endOfDay(DateUtil.date()));
        shopFlowInfoVO.setVisitUserCount((long) shopUserIds.size());
        Set<String> shopUserIdsSet = new HashSet<>(shopUserIds);
        // 求集合的交集得到店铺的新访客
        shopUserIdsSet.retainAll(newUserIds);
        shopFlowInfoVO.setNewVisitUserCount((long) shopUserIdsSet.size());
        shopFlowInfoVO.setOldVisitUserCount(shopFlowInfoVO.getVisitUserCount() - shopFlowInfoVO.getNewVisitUserCount());
        return shopFlowInfoVO;

    }

    private PageVO<ProdEffectRespVO> getFlowSpuId(PageDTO pageDTO, Long startTime, Long endTime, Long shopId) {
        return PageUtil.doPage(pageDTO, () -> productAnalyseMapper.getProdEffectRespByTime(new Date(startTime), new Date(endTime), shopId));
    }

    @Override
    public ShopFlowInfoVO getSupplierFlowInfoAndProdRankIng(Long supplierId, DateTime endTime, Integer dayCount, Integer limit) {
        // 获取商家代销商品Id
        ServerResponseEntity<List<SpuVO>> merchantSpuIdsResponseEntity = productFeignClient.listMerchantSpuIdsBySupplierId(supplierId);
        if (merchantSpuIdsResponseEntity.isFail()) {
            throw new LuckException(merchantSpuIdsResponseEntity.getMsg());
        }
        List<SpuVO> spuVOList = merchantSpuIdsResponseEntity.getData();
        if (CollectionUtil.isEmpty(spuVOList)) {
            return new ShopFlowInfoVO();
        }
        List<Long> spuIds = spuVOList.stream().map(SpuVO::getSpuId).collect(Collectors.toList());
        // 商品浏览量
        ShopFlowInfoVO shopFlowInfo = getSupplierFlowInfo(spuIds);
        // 商品访客榜单
        shopFlowInfo.setProdRanking(listSupplierProdRankIngByFlow(spuVOList, endTime, dayCount, limit));
        return shopFlowInfo;
    }

    private Double divAverage(Integer a, Integer b, Integer scale) {
        if (Objects.isNull(b) || b == 0 || Objects.isNull(a)) {
            return 0.0;
        }
        return Arith.div(a, b, scale);
    }

    private Double getDefault(Double value) {
        if (Objects.nonNull(value)) {
            return value;
        }
        return 0D;
    }

    private Integer getDefault(Integer value) {
        if (Objects.nonNull(value)) {
            return value;
        }
        return Constant.ZERO_INTEGER;
    }
}
